package com.dofast.bt.pulltorefresh;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.OverScroller;
import android.widget.ScrollView;

import com.dofast.bt.R;
import com.dofast.bt.pulltorefresh.internal.FlipLoadingLayout;
import com.dofast.bt.pulltorefresh.internal.LoadingLayout;
import com.dofast.bt.pulltorefresh.internal.RotateLoadingLayout;
import com.dofast.bt.pulltorefresh.internal.Utils;
import com.dofast.bt.pulltorefresh.internal.ViewCompat;


public class StickyNavLayout2 extends LinearLayout implements IPullToRefresh<LinearLayout> {

    /**********************/
    // ===========================================================
    // Constants
    // ===========================================================

    static final boolean DEBUG = false;

    static final boolean USE_HW_LAYERS = false;

    static final String LOG_TAG = "PullToRefresh";

    static final float FRICTION = 2.0f;

    public static final int SMOOTH_SCROLL_DURATION_MS = 200;
    public static final int SMOOTH_SCROLL_LONG_DURATION_MS = 325;
    static final int DEMO_SCROLL_INTERVAL = 225;

    static final String STATE_STATE = "ptr_state";
    static final String STATE_MODE = "ptr_mode";
    static final String STATE_CURRENT_MODE = "ptr_current_mode";
    static final String STATE_SCROLLING_REFRESHING_ENABLED = "ptr_disable_scrolling";
    static final String STATE_SHOW_REFRESHING_VIEW = "ptr_show_refreshing_view";
    static final String STATE_SUPER = "ptr_super";

    // ===========================================================
    // Fields
    // ===========================================================
    private boolean isOpenRefreshSwitch = false;

    private int mTouchSlop;
    private float mLastMotionX, mLastMotionY;
    private float mInitialMotionX, mInitialMotionY;

    private boolean mIsBeingDragged = false;
    private PullToRefreshBase.State mState = PullToRefreshBase.State.RESET;
    private PullToRefreshBase.Mode mMode = PullToRefreshBase.Mode.getDefault();

    private PullToRefreshBase.Mode mCurrentMode;
    LinearLayout mRefreshableView;
    private FrameLayout mRefreshableViewWrapper;

    private boolean mShowViewWhileRefreshing = true;
    private boolean mScrollingWhileRefreshingEnabled = false;
    private boolean mFilterTouchEvents = true;
    private boolean mOverScrollEnabled = true;
    private boolean mLayoutVisibilityChangesEnabled = true;

    private Interpolator mScrollAnimationInterpolator;
    private AnimationStyle mLoadingAnimationStyle = AnimationStyle.FLIP;
    ;// = AnimationStyle.getDefault();

    private LoadingLayout mHeaderLayout;
    private LoadingLayout mFooterLayout;

    private OnRefreshListener mOnRefreshListener;
    private OnRefreshListener2 mOnRefreshListener2;
    private OnPullEventListener mOnPullEventListener;

    private SmoothScrollRunnable mCurrentSmoothScrollRunnable;

    /**********************/

    private View mTop;
    private View mNav;
    private ViewPager mViewPager;

    private int mTopViewHeight;
    private ViewGroup mInnerScrollView;
    private boolean isTopHidden = false;

    private OverScroller mScroller;
    private VelocityTracker mVelocityTracker;
    //	private int mTouchSlop2;
    private int mMaximumVelocity, mMinimumVelocity;

    private float mLastY;
    private boolean mDragging;

    private boolean isInControl = false;

    public StickyNavLayout2(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(LinearLayout.VERTICAL);

        mScroller = new OverScroller(context);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        mMaximumVelocity = ViewConfiguration.get(context)
                .getScaledMaximumFlingVelocity();
        mMinimumVelocity = ViewConfiguration.get(context)
                .getScaledMinimumFlingVelocity();

        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        switch (getPullToRefreshScrollDirection()) {
            case HORIZONTAL:
                setOrientation(LinearLayout.HORIZONTAL);
                break;
            case VERTICAL:
            default:
                setOrientation(LinearLayout.VERTICAL);
                break;
        }

        setGravity(Gravity.CENTER);

        ViewConfiguration config = ViewConfiguration.get(context);
//		mTouchSlop = config.getScaledTouchSlop();

        // Styleables from XML
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PullToRefresh);

        if (a.hasValue(R.styleable.PullToRefresh_ptrMode)) {
            mMode = PullToRefreshBase.Mode.mapIntToValue(a.getInteger(R.styleable.PullToRefresh_ptrMode, 0));
        }

        if (a.hasValue(R.styleable.PullToRefresh_ptrAnimationStyle)) {
            mLoadingAnimationStyle = AnimationStyle.mapIntToValue(a.getInteger(
                    R.styleable.PullToRefresh_ptrAnimationStyle, 0));
        }

        // Refreshable View
        // By passing the attrs, we can add ListView/GridView params via XML
        mRefreshableView = createRefreshableView(context, attrs);
        addRefreshableView(context, mRefreshableView);

        // We need to create now layouts now
        mHeaderLayout = createLoadingLayout(context, PullToRefreshBase.Mode.PULL_FROM_START, a);
        mFooterLayout = createLoadingLayout(context, PullToRefreshBase.Mode.PULL_FROM_END, a);

        /**
         * Styleables from XML
         */
        if (a.hasValue(R.styleable.PullToRefresh_ptrRefreshableViewBackground)) {
            Drawable background = a.getDrawable(R.styleable.PullToRefresh_ptrRefreshableViewBackground);
            if (null != background) {
                mRefreshableView.setBackgroundDrawable(background);
            }
        } else if (a.hasValue(R.styleable.PullToRefresh_ptrAdapterViewBackground)) {
            Utils.warnDeprecation("ptrAdapterViewBackground", "ptrRefreshableViewBackground");
            Drawable background = a.getDrawable(R.styleable.PullToRefresh_ptrAdapterViewBackground);
            if (null != background) {
                mRefreshableView.setBackgroundDrawable(background);
            }
        }

        if (a.hasValue(R.styleable.PullToRefresh_ptrOverScroll)) {
            mOverScrollEnabled = a.getBoolean(R.styleable.PullToRefresh_ptrOverScroll, true);
        }

        if (a.hasValue(R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled)) {
            mScrollingWhileRefreshingEnabled = a.getBoolean(
                    R.styleable.PullToRefresh_ptrScrollingWhileRefreshingEnabled, false);
        }

        // Let the derivative classes have a go at handling attributes, then
        // recycle them...
        handleStyledAttributes(a);
        a.recycle();

        // Finally update the UI for the modes
        updateUIForMode();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mTop = findViewById(R.id.id_stickynavlayout_topview);
        mNav = findViewById(R.id.id_stickynavlayout_indicator);
        View view = findViewById(R.id.id_stickynavlayout_viewpager);
        if (!(view instanceof ViewPager)) {
            throw new RuntimeException(
                    "id_stickynavlayout_viewpager show used by ViewPager !");
        }
        mViewPager = (ViewPager) view;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ViewGroup.LayoutParams params = mViewPager.getLayoutParams();
        params.height = getMeasuredHeight() - mNav.getMeasuredHeight();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // We need to update the header/footer when our size changes
        refreshLoadingViewsSize();

        // Update the Refreshable View layout_coupon_bg
        refreshRefreshableViewSize(w, h);

        /**
         * As we're currently in a Layout Pass, we need to schedule another one
         * to layout_coupon_bg any changes we've made here
         */
        post(new Runnable() {
            @Override
            public void run() {
                requestLayout();
            }
        });
        mTopViewHeight = mTop.getMeasuredHeight();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        float y = ev.getY();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                float dy = y - mLastY;
                Rect topRect = new Rect();
                mTop.getLocalVisibleRect(topRect);
//			Helper.log("top===" + topRect.top + "  dy=" + dy + " top=" + getTop() + "  Y=" + getY());
                if (!isOpenRefreshSwitch && topRect.top == 0 && dy > 0) {
                    isOpenRefreshSwitch = true;
                } else {
                    isOpenRefreshSwitch = false;

                    getCurrentScrollView();

                    if (mInnerScrollView instanceof ScrollView) {
                        if (mInnerScrollView.getScrollY() == 0 && isTopHidden && dy > 0
                                && !isInControl) {
                            isInControl = true;
                            ev.setAction(MotionEvent.ACTION_CANCEL);
                            MotionEvent ev2 = MotionEvent.obtain(ev);
                            dispatchTouchEvent(ev);
                            ev2.setAction(MotionEvent.ACTION_DOWN);
                            return dispatchTouchEvent(ev2);
                        }
                    } else if (mInnerScrollView instanceof ListView) {

                        ListView lv = (ListView) mInnerScrollView;
                        View c = lv.getChildAt(lv.getFirstVisiblePosition());

                        if (!isInControl && c != null && c.getTop() == 0 && isTopHidden
                                && dy > 0) {
                            isInControl = true;
                            ev.setAction(MotionEvent.ACTION_CANCEL);
                            MotionEvent ev2 = MotionEvent.obtain(ev);
                            dispatchTouchEvent(ev);
                            ev2.setAction(MotionEvent.ACTION_DOWN);
                            return dispatchTouchEvent(ev2);
                        }
                    } else if (mInnerScrollView instanceof PullToRefreshGridView) {

                        PullToRefreshGridView rv = (PullToRefreshGridView) mInnerScrollView;

                        if (!isInControl && android.support.v4.view.ViewCompat.canScrollVertically(rv, -1) && isTopHidden
                                && dy > 0) {
                            isInControl = true;
                            ev.setAction(MotionEvent.ACTION_CANCEL);
                            MotionEvent ev2 = MotionEvent.obtain(ev);
                            dispatchTouchEvent(ev);
                            ev2.setAction(MotionEvent.ACTION_DOWN);
                            return dispatchTouchEvent(ev2);
                        }
                    } else if (mInnerScrollView instanceof GridView) {

                        GridView rv = (GridView) mInnerScrollView;
//				Helper.log("isInControl="+isInControl + " 是否可滑动:"+android.support.v4.view.ViewCompat.canScrollVertically(rv, -1) + " isTopHidden="+isTopHidden + " dy="+dy);
                        if (!isInControl && android.support.v4.view.ViewCompat.canScrollVertically(rv, -1) && isTopHidden
                                && dy > 0) {
                            isInControl = true;
                            ev.setAction(MotionEvent.ACTION_CANCEL);
                            MotionEvent ev2 = MotionEvent.obtain(ev);
                            dispatchTouchEvent(ev);
                            ev2.setAction(MotionEvent.ACTION_DOWN);
                            return dispatchTouchEvent(ev2);
                        } else if (isInControl && !android.support.v4.view.ViewCompat.canScrollVertically(rv, -1) && isTopHidden
                                && dy > 0) {
//					Helper.log("-------------------------------- dispatchTouchEvent --------------------------------");
                            initVelocityTrackerIfNotExists();
                            mVelocityTracker.addMovement(ev);
//					ev.setAction(MotionEvent.ACTION_CANCEL);
//					MotionEvent ev2 = MotionEvent.obtain(ev);
//					dispatchTouchEvent(ev);
                            mLastY = y;
//					ev2.setAction(MotionEvent.ACTION_DOWN);
                            return super.dispatchTouchEvent(ev);
                        }
                    }
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    /**
     *
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();
        float y = ev.getY();
        if (isOpenRefreshSwitch) {
            if (isPullToRefreshEnabled()) {
                if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
                    mIsBeingDragged = false;
                } else {
                    if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {
                        return true;
                    }
                }
            }
        }

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (isOpenRefreshSwitch && isReadyForPull()) {
                    mLastMotionY = mInitialMotionY = ev.getY();
                    mLastMotionX = mInitialMotionX = ev.getX();
                    mIsBeingDragged = false;
                    return mIsBeingDragged;
                } else {
                    mLastY = y;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                /***********************************************************/
                if (isOpenRefreshSwitch) {
                    // If we're refreshing, and the flag is set. Eat all MOVE events
                    if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
                        return true;
                    }
                    if (isReadyForPull()) {
                        final float x = ev.getX();
                        final float diff, oppositeDiff, absDiff;

                        // We need to use the correct values, based on scroll
                        // direction
                        switch (getPullToRefreshScrollDirection()) {
                            case HORIZONTAL:
                                diff = x - mLastMotionX;
                                oppositeDiff = y - mLastMotionY;
                                break;
                            case VERTICAL:
                            default:
                                diff = y - mLastMotionY;
                                oppositeDiff = x - mLastMotionX;
                                break;
                        }
                        absDiff = Math.abs(diff);

                        if (absDiff > mTouchSlop && (!mFilterTouchEvents || absDiff > Math.abs(oppositeDiff))) {
                            if (mMode.showHeaderLoadingLayout() && diff >= 1f && isReadyForPullStart()) {
                                mLastMotionY = y;
                                mLastMotionX = x;
                                mIsBeingDragged = true;
                                if (mMode == PullToRefreshBase.Mode.BOTH) {
                                    mCurrentMode = PullToRefreshBase.Mode.PULL_FROM_START;
                                }
                            } else if (mMode.showFooterLoadingLayout() && diff <= -1f && isReadyForPullEnd()) {
                                mLastMotionY = y;
                                mLastMotionX = x;
                                mIsBeingDragged = true;
                                if (mMode == PullToRefreshBase.Mode.BOTH) {
                                    mCurrentMode = PullToRefreshBase.Mode.PULL_FROM_END;
                                }
                            }
                        }
                    }
                    return mIsBeingDragged;
                } else {
                    /***********************************************************/
                    float dy = y - mLastY;

                    getCurrentScrollView();
                    if (Math.abs(dy) > mTouchSlop) {
                        mDragging = true;
                        if (mInnerScrollView instanceof ScrollView) {
                            // 如果topView没有隐藏
                            // 或sc的scrollY = 0 && topView隐藏 && 下拉，则拦截
                            if (!isTopHidden
                                    || (mInnerScrollView.getScrollY() == 0
                                    && isTopHidden && dy > 0)) {

                                initVelocityTrackerIfNotExists();
                                mVelocityTracker.addMovement(ev);
                                mLastY = y;
                                return true;
                            }
                        } else if (mInnerScrollView instanceof ListView) {

                            ListView lv = (ListView) mInnerScrollView;
                            View c = lv.getChildAt(lv.getFirstVisiblePosition());
                            // 如果topView没有隐藏
                            // 或sc的listView在顶部 && topView隐藏 && 下拉，则拦截

                            if (!isTopHidden || //
                                    (c != null //
                                            && c.getTop() == 0//
                                            && isTopHidden && dy > 0)) {

                                initVelocityTrackerIfNotExists();
                                mVelocityTracker.addMovement(ev);
                                mLastY = y;
                                return true;
                            }
                        } else if (mInnerScrollView instanceof PullToRefreshGridView) {
                            PullToRefreshGridView rv = (PullToRefreshGridView) mInnerScrollView;
                            GridView lv = ((PullToRefreshGridView) mInnerScrollView)
                                    .getRefreshableView();
                            View c = lv.getChildAt(lv.getFirstVisiblePosition());
                            if (!isTopHidden || //
                                    (c != null //
                                            && c.getTop() == 0//
                                            && isTopHidden && dy > 0)) {

                                initVelocityTrackerIfNotExists();
                                mVelocityTracker.addMovement(ev);
                                mLastY = y;
                                return true;
                            }
                        } else if (mInnerScrollView instanceof GridView) {
                            GridView rv = (GridView) mInnerScrollView;
                            View c = rv.getChildAt(rv.getFirstVisiblePosition());
                            Rect r = new Rect();
//					Helper.log("isTopHidden="+isTopHidden + " position="+rv.getFirstVisiblePosition() + " c.getY="+ r.top + "  dy="+dy);
                            if (!isTopHidden || (c != null && c.getTop() == 0 && isTopHidden && dy > 0)) {
                                initVelocityTrackerIfNotExists();
                                mVelocityTracker.addMovement(ev);
                                mLastY = y;
                                return true;
                            } else if (isTopHidden && rv.getFirstVisiblePosition() == 0 && (c != null && c.getLocalVisibleRect(r) && r.top == 0) && dy >= 0) {
                                scrollBy(0, (int) -dy);
                                return super.onInterceptTouchEvent(ev);
                            }
                        }
                    } else {
                        if (mInnerScrollView instanceof GridView) {
                            GridView rv = (GridView) mInnerScrollView;
                            View c = rv.getChildAt(rv.getFirstVisiblePosition());
                            Rect r = new Rect();
//					Helper.log("isTopHidden="+isTopHidden + " position="+rv.getFirstVisiblePosition() + " c.getY="+ r.top + "  dy="+dy);
                            if (isTopHidden && rv.getFirstVisiblePosition() == 0 && (c != null && c.getLocalVisibleRect(r) && r.top == 0) && dy >= 0f) {
                                isTopHidden = false;
                                mLastY = y;
                                return super.onInterceptTouchEvent(ev);
                            }
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                mDragging = false;
                recycleVelocityTracker();
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    private void getCurrentScrollView() {

        int currentItem = mViewPager.getCurrentItem();
        PagerAdapter a = mViewPager.getAdapter();
        if (a instanceof FragmentPagerAdapter) {
            FragmentPagerAdapter fadapter = (FragmentPagerAdapter) a;
            Fragment item = (Fragment) fadapter.instantiateItem(mViewPager,
                    currentItem);
            mInnerScrollView = (ViewGroup) (item.getView()
                    .findViewById(R.id.id_stickynavlayout_innerscrollview));
        } else if (a instanceof FragmentStatePagerAdapter) {
            FragmentStatePagerAdapter fsAdapter = (FragmentStatePagerAdapter) a;
            Fragment item = (Fragment) fsAdapter.instantiateItem(mViewPager,
                    currentItem);
            mInnerScrollView = (ViewGroup) (item.getView()
                    .findViewById(R.id.id_stickynavlayout_innerscrollview));
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        float y = event.getY();
        /*********************************************************************/
        if (isOpenRefreshSwitch) {

            if (!isPullToRefreshEnabled()) {
                return false;
            }
            // If we're refreshing, and the flag is set. Eat the event
            if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
                return true;
            }
            if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
                return false;
            }
            if (isPullToRefreshEnabled()) {
                // If we're refreshing, and the flag is set. Eat the event
                if (!mScrollingWhileRefreshingEnabled && isRefreshing()) {
                    return true;
                }
                if (!(event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0)) {
                    return true;
                }
            }
        }
        /*********************************************************************/
        initVelocityTrackerIfNotExists();
        mVelocityTracker.addMovement(event);

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                if (isOpenRefreshSwitch) {
                    if (isReadyForPull()) {
                        mLastMotionY = mInitialMotionY = event.getY();
                        mLastMotionX = mInitialMotionX = event.getX();
                        return true;
                    }
                } else {
                    if (!mScroller.isFinished()) {
                        mScroller.abortAnimation();
                    }
                    mLastY = y;
                }
                return true;
            case MotionEvent.ACTION_MOVE:
                if (isOpenRefreshSwitch) {
                    if (mIsBeingDragged) {
                        mLastMotionY = event.getY();
                        mLastMotionX = event.getX();
                        pullEvent();
                        return true;
                    }
                } else {
                    float dy = y - mLastY;

                    if (!mDragging && Math.abs(dy) > mTouchSlop) {
                        mDragging = true;
                    }
                    if (mDragging) {
                        scrollBy(0, (int) -dy);

                        // 如果topView隐藏，且上滑动时，则改变当前事件为ACTION_DOWN
                        if (getScrollY() == mTopViewHeight && dy < 0) {
                            event.setAction(MotionEvent.ACTION_DOWN);
                            dispatchTouchEvent(event);
                            isInControl = false;
                        } else if (getScrollY() == mTopViewHeight && dy > 0) {
                            if (mInnerScrollView instanceof GridView) {
                                GridView rv = (GridView) mInnerScrollView;
                                View child = rv.getChildAt(0);
                                View c = rv.getChildAt(rv.getFirstVisiblePosition());
                                if (c != null && c.getTop() == 0 && isTopHidden && dy > 0) {
                                    return super.onTouchEvent(event);
                                }
                            }
                        }
                    }
                    mLastY = y;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                if (isOpenRefreshSwitch) {
                    if (mIsBeingDragged) {
                        mIsBeingDragged = false;

                        if (mState == PullToRefreshBase.State.RELEASE_TO_REFRESH
                                && (null != mOnRefreshListener || null != mOnRefreshListener2)) {
                            setState(PullToRefreshBase.State.REFRESHING, true);
                            return true;
                        }

                        // If we're already refreshing, just scroll back to the top
                        if (isRefreshing()) {
                            smoothScrollTo(0);
                            return true;
                        }

                        // If we haven't returned by here, then we're not in a state
                        // to pull, so just reset
                        setState(PullToRefreshBase.State.RESET);

                        return true;
                    }
                } else {
                    mDragging = false;
                    recycleVelocityTracker();
                    if (!mScroller.isFinished()) {
                        mScroller.abortAnimation();
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (isOpenRefreshSwitch) {
                    if (mIsBeingDragged) {
                        mIsBeingDragged = false;

                        if (mState == PullToRefreshBase.State.RELEASE_TO_REFRESH
                                && (null != mOnRefreshListener || null != mOnRefreshListener2)) {
                            setState(PullToRefreshBase.State.REFRESHING, true);
                            return true;
                        }

                        // If we're already refreshing, just scroll back to the top
                        if (isRefreshing()) {
                            smoothScrollTo(0);
                            return true;
                        }

                        // If we haven't returned by here, then we're not in a state
                        // to pull, so just reset
                        setState(PullToRefreshBase.State.RESET);

                        return true;
                    }
                } else {
                    mDragging = false;
                    mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int velocityY = (int) mVelocityTracker.getYVelocity();
                    if (Math.abs(velocityY) > mMinimumVelocity) {
                        fling(-velocityY);
                    }
                    recycleVelocityTracker();
                }
                break;
        }

        return super.onTouchEvent(event);
    }

    public void fling(int velocityY) {
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
        invalidate();
    }

    @Override
    public void scrollTo(int x, int y) {
        if (y < 0) {
            y = 0;
        }
        if (y > mTopViewHeight) {
            y = mTopViewHeight;
        }
        if (y != getScrollY()) {
            super.scrollTo(x, y);
        }

        isTopHidden = getScrollY() == mTopViewHeight;

    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }

    private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    /*************************************************************/
    /**
     * Actions a Pull Event
     *
     * @return true if the Event has been handled, false if there has been no
     * change
     */
    private void pullEvent() {
        final int newScrollValue;
        final int itemDimension;
        final float initialMotionValue, lastMotionValue;

        switch (getPullToRefreshScrollDirection()) {
            case HORIZONTAL:
                initialMotionValue = mInitialMotionX;
                lastMotionValue = mLastMotionX;
                break;
            case VERTICAL:
            default:
                initialMotionValue = mInitialMotionY;
                lastMotionValue = mLastMotionY;
                break;
        }

        switch (mCurrentMode) {
            case PULL_FROM_END:
                newScrollValue = Math.round(Math.max(initialMotionValue - lastMotionValue, 0) / FRICTION);
                itemDimension = getFooterSize();
                break;
            case PULL_FROM_START:
            default:
                newScrollValue = Math.round(Math.min(initialMotionValue - lastMotionValue, 0) / FRICTION);
                itemDimension = getHeaderSize();
                break;
        }

        setHeaderScroll(newScrollValue);

        if (newScrollValue != 0 && !isRefreshing()) {
            float scale = Math.abs(newScrollValue) / (float) itemDimension;
            switch (mCurrentMode) {
                case PULL_FROM_END:
                    mFooterLayout.onPull(scale);
                    break;
                case PULL_FROM_START:
                default:
                    mHeaderLayout.onPull(scale);
                    break;
            }

            if (mState != PullToRefreshBase.State.PULL_TO_REFRESH && itemDimension >= Math.abs(newScrollValue)) {
                setState(PullToRefreshBase.State.PULL_TO_REFRESH);
            } else if (mState == PullToRefreshBase.State.PULL_TO_REFRESH && itemDimension < Math.abs(newScrollValue)) {
                setState(PullToRefreshBase.State.RELEASE_TO_REFRESH);
            }
        }
    }

    final void setState(PullToRefreshBase.State state, final boolean... params) {
        mState = state;
        if (DEBUG) {
            Log.d(LOG_TAG, "State: " + mState.name());
        }

        switch (mState) {
            case RESET:
                onReset();
                break;
            case PULL_TO_REFRESH:
                onPullToRefresh();
                break;
            case RELEASE_TO_REFRESH:
                onReleaseToRefresh();
                break;
            case REFRESHING:
            case MANUAL_REFRESHING:
                onRefreshing(params[0]);
                break;
            case OVERSCROLLING:
                // NO-OP
                break;
        }

        // Call OnPullEventListener
        if (null != mOnPullEventListener) {
            mOnPullEventListener.onPullEvent(this, mState, mCurrentMode);
        }
    }

    /**
     * Called when the UI has been to be updated to be in the
     */
    protected void onPullToRefresh() {
        switch (mCurrentMode) {
            case PULL_FROM_END:
                mFooterLayout.pullToRefresh();
                break;
            case PULL_FROM_START:
                mHeaderLayout.pullToRefresh();
                break;
            default:
                // NO-OP
                break;
        }
    }

    /**
     * Called when the UI has been to be updated to be in the
     *
     * @param doScroll - Whether the UI should scroll for this event.
     */
    protected void onRefreshing(final boolean doScroll) {
        if (mMode.showHeaderLoadingLayout()) {
            mHeaderLayout.refreshing();
        }
        if (mMode.showFooterLoadingLayout()) {
            mFooterLayout.refreshing();
        }

        if (doScroll) {
            if (mShowViewWhileRefreshing) {

                // Call Refresh Listener when the Scroll has finished
                OnSmoothScrollFinishedListener listener = new OnSmoothScrollFinishedListener() {
                    @Override
                    public void onSmoothScrollFinished() {
                        callRefreshListener();
                    }
                };

                switch (mCurrentMode) {
                    case MANUAL_REFRESH_ONLY:
                    case PULL_FROM_END:
                        smoothScrollTo(getFooterSize(), listener);
                        break;
                    default:
                    case PULL_FROM_START:
                        smoothScrollTo(-getHeaderSize(), listener);
                        break;
                }
            } else {
                smoothScrollTo(0);
            }
        } else {
            // We're not scrolling, so just call Refresh Listener now
            callRefreshListener();
        }
    }

    private void callRefreshListener() {
        if (null != mOnRefreshListener) {
            mOnRefreshListener.onRefresh(this);
        } else if (null != mOnRefreshListener2) {
            if (mCurrentMode == PullToRefreshBase.Mode.PULL_FROM_START) {
                mOnRefreshListener2.onPullDownToRefresh(this);
            } else if (mCurrentMode == PullToRefreshBase.Mode.PULL_FROM_END) {
                mOnRefreshListener2.onPullUpToRefresh(this);
            }
        }
    }

    /**
     * Called when the UI has been to be updated to be in the
     */
    protected void onReleaseToRefresh() {
        switch (mCurrentMode) {
            case PULL_FROM_END:
                mFooterLayout.releaseToRefresh();
                break;
            case PULL_FROM_START:
                mHeaderLayout.releaseToRefresh();
                break;
            default:
                // NO-OP
                break;
        }
    }

    /**
     * Called when the UI has been to be updated to be in the
     */
    protected void onReset() {
        mIsBeingDragged = false;
        mLayoutVisibilityChangesEnabled = true;

        // Always reset both layouts, just in case...
        mHeaderLayout.reset();
        mFooterLayout.reset();

        smoothScrollTo(0);
    }

    protected final LoadingLayout getFooterLayout() {
        return mFooterLayout;
    }

    protected final int getFooterSize() {
        return mFooterLayout.getContentSize();
    }

    protected final LoadingLayout getHeaderLayout() {
        return mHeaderLayout;
    }

    protected final int getHeaderSize() {
        return mHeaderLayout.getContentSize();
    }

    @Override
    public boolean demo() {
        return false;
    }

    @Override
    public PullToRefreshBase.Mode getCurrentMode() {
        return mCurrentMode;
    }

    @Override
    public boolean getFilterTouchEvents() {
        return mFilterTouchEvents;
    }

    @Override
    public ILoadingLayout getLoadingLayoutProxy() {
        return getLoadingLayoutProxy(true, true);
    }

    @Override
    public ILoadingLayout getLoadingLayoutProxy(boolean includeStart, boolean includeEnd) {
        return createLoadingLayoutProxy(includeStart, includeEnd);
    }

    @Override
    public PullToRefreshBase.Mode getMode() {
        return mMode;
    }

    @Override
    public LinearLayout getRefreshableView() {
        return mRefreshableView;
    }

    @Override
    public boolean getShowViewWhileRefreshing() {
        return mShowViewWhileRefreshing;
    }

    @Override
    public final PullToRefreshBase.State getState() {
        return mState;
    }

    @Override
    public boolean isPullToRefreshEnabled() {
        return mMode.permitsPullToRefresh();
    }

    @Override
    public boolean isPullToRefreshOverScrollEnabled() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD && mOverScrollEnabled
                && OverscrollHelper.isAndroidOverScrollEnabled(mRefreshableView);
    }

    @Override
    public boolean isRefreshing() {
        return mState == PullToRefreshBase.State.REFRESHING || mState == PullToRefreshBase.State.MANUAL_REFRESHING;
    }

    @Override
    public boolean isScrollingWhileRefreshingEnabled() {
        return mScrollingWhileRefreshingEnabled;
    }

    @Override
    public void onRefreshComplete() {
        if (isRefreshing()) {
            setState(PullToRefreshBase.State.RESET);
        }
    }

    @Override
    public void setFilterTouchEvents(boolean filterEvents) {
        mFilterTouchEvents = filterEvents;
    }

    @Override
    public void setMode(PullToRefreshBase.Mode mode) {
        if (mode != mMode) {
            if (DEBUG) {
                Log.d(LOG_TAG, "Setting mode to: " + mode);
            }
            mMode = mode;
            updateUIForMode();
        }
    }

    public void setOnRefreshListenerSticky(final StickyNavLayout2.OnRefreshListener2 listener) {
        this.mOnRefreshListener2 = listener;
    }

    @Override
    public void setOnPullEventListener(PullToRefreshBase.OnPullEventListener<LinearLayout> listener) {

    }

    @Override
    public void setOnRefreshListener(final PullToRefreshBase.OnRefreshListener<LinearLayout> listener) {

    }

    @Override
    public void setOnRefreshListener(PullToRefreshBase.OnRefreshListener2<LinearLayout> listener) {
    }

    /**
     * Used internally for {@link #getLoadingLayoutProxy(boolean, boolean)}.
     * Allows derivative classes to include any extra LoadingLayouts.
     */
    protected LoadingLayoutProxy createLoadingLayoutProxy(final boolean includeStart, final boolean includeEnd) {
        LoadingLayoutProxy proxy = new LoadingLayoutProxy();

        if (includeStart && mMode.showHeaderLoadingLayout()) {
            proxy.addLayout(mHeaderLayout);
        }
        if (includeEnd && mMode.showFooterLoadingLayout()) {
            proxy.addLayout(mFooterLayout);
        }

        return proxy;
    }

    @Override
    public void setPullToRefreshOverScrollEnabled(boolean enabled) {
        mOverScrollEnabled = enabled;
    }

    @Override
    public void setRefreshing() {
        setRefreshing(true);
    }

    @Override
    public void setRefreshing(boolean doScroll) {
        if (!isRefreshing()) {
            setState(PullToRefreshBase.State.MANUAL_REFRESHING, doScroll);
        }
    }

    @Override
    public void setScrollAnimationInterpolator(Interpolator interpolator) {
        mScrollAnimationInterpolator = interpolator;
    }

    @Override
    public void setScrollingWhileRefreshingEnabled(boolean scrollingWhileRefreshingEnabled) {
        mScrollingWhileRefreshingEnabled = scrollingWhileRefreshingEnabled;
    }

    @Override
    public void setShowViewWhileRefreshing(boolean showView) {
        mShowViewWhileRefreshing = showView;
    }

    protected LinearLayout createRefreshableView(Context context, AttributeSet attrs) {
        LinearLayout linearLayout = new LinearLayout(context, attrs);
        return linearLayout;
    }

    private void addRefreshableView(Context context, LinearLayout refreshableView) {
        mRefreshableViewWrapper = new FrameLayout(context);
        mRefreshableViewWrapper.addView(refreshableView, ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);

        addViewInternal(mRefreshableViewWrapper, new LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT));
    }

    protected final void addViewInternal(View child, ViewGroup.LayoutParams params) {
        super.addView(child, -1, params);
    }

    protected final void addViewInternal(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
    }

    protected LoadingLayout createLoadingLayout(Context context, PullToRefreshBase.Mode mode, TypedArray attrs) {
        LoadingLayout layout = mLoadingAnimationStyle.createLoadingLayout(context, mode,
                getPullToRefreshScrollDirection(), attrs);
        layout.setVisibility(View.INVISIBLE);
        return layout;
    }

    public final PullToRefreshBase.Orientation getPullToRefreshScrollDirection() {
        return PullToRefreshBase.Orientation.VERTICAL;
    }

    protected void handleStyledAttributes(TypedArray a) {
    }

    protected final void refreshLoadingViewsSize() {
        final int maximumPullScroll = (int) (getMaximumPullScroll() * 1.2f);

        int pLeft = getPaddingLeft();
        int pTop = getPaddingTop();
        int pRight = getPaddingRight();
        int pBottom = getPaddingBottom();

        switch (getPullToRefreshScrollDirection()) {
            case HORIZONTAL:
                if (mMode.showHeaderLoadingLayout()) {
                    mHeaderLayout.setWidth(maximumPullScroll);
                    pLeft = -maximumPullScroll;
                } else {
                    pLeft = 0;
                }

                if (mMode.showFooterLoadingLayout()) {
                    mFooterLayout.setWidth(maximumPullScroll);
                    pRight = -maximumPullScroll;
                } else {
                    pRight = 0;
                }
                break;

            case VERTICAL:
                if (mMode.showHeaderLoadingLayout()) {
                    mHeaderLayout.setHeight(maximumPullScroll);
                    pTop = -maximumPullScroll;
                } else {
                    pTop = 0;
                }

                if (mMode.showFooterLoadingLayout()) {
                    mFooterLayout.setHeight(maximumPullScroll);
                    pBottom = -maximumPullScroll;
                } else {
                    pBottom = 0;
                }
                break;
        }

        if (DEBUG) {
            Log.d(LOG_TAG, String.format("Setting Padding. L: %d, T: %d, R: %d, B: %d", pLeft, pTop, pRight, pBottom));
        }
        setPadding(pLeft, pTop, pRight, pBottom);
    }

    protected final void refreshRefreshableViewSize(int width, int height) {
        // We need to set the Height of the Refreshable View to the same as
        // this layout_coupon_bg
        LayoutParams lp = (LayoutParams) mRefreshableViewWrapper.getLayoutParams();

        switch (getPullToRefreshScrollDirection()) {
            case HORIZONTAL:
                if (lp.width != width) {
                    lp.width = width;
                    mRefreshableViewWrapper.requestLayout();
                }
                break;
            case VERTICAL:
                if (lp.height != height) {
                    lp.height = height;
                    mRefreshableViewWrapper.requestLayout();
                }
                break;
        }
    }

    private boolean isReadyForPull() {
        switch (mMode) {
            case PULL_FROM_START:
                return isReadyForPullStart();
            case PULL_FROM_END:
                return isReadyForPullEnd();
            case BOTH:
                return isReadyForPullEnd() || isReadyForPullStart();
            default:
                return false;
        }
    }

    private boolean isReadyForPullStart() {
        return mRefreshableView.getScrollY() == 0;
    }

    private boolean isReadyForPullEnd() {
        View scrollViewChild = mRefreshableView.getChildAt(0);
        if (null != scrollViewChild) {
            return mRefreshableView.getScrollY() >= (scrollViewChild.getHeight() - getHeight());
        }
        return false;
    }

    /**
     * Helper method which just calls scrollTo() in the correct scrolling
     * direction.
     *
     * @param value - New Scroll value
     */
    protected final void setHeaderScroll(int value) {
        if (DEBUG) {
            Log.d(LOG_TAG, "setHeaderScroll: " + value);
        }

        // Clamp value to with pull scroll range
        final int maximumPullScroll = getMaximumPullScroll();
        value = Math.min(maximumPullScroll, Math.max(-maximumPullScroll, value));

        if (mLayoutVisibilityChangesEnabled) {
            if (value < 0) {
                mHeaderLayout.setVisibility(View.VISIBLE);
            } else if (value > 0) {
                mFooterLayout.setVisibility(View.VISIBLE);
            } else {
                mHeaderLayout.setVisibility(View.INVISIBLE);
                mFooterLayout.setVisibility(View.INVISIBLE);
            }
        }

        if (USE_HW_LAYERS) {
            /**
             * Use a Hardware Layer on the Refreshable View if we've scrolled at
             * all. We don't use them on the Header/Footer Views as they change
             * often, which would negate any HW layer performance boost.
             */
            ViewCompat.setLayerType(mRefreshableViewWrapper, value != 0 ? View.LAYER_TYPE_HARDWARE
                    : View.LAYER_TYPE_NONE);
        }

        switch (getPullToRefreshScrollDirection()) {
            case VERTICAL:
                scrollTo(0, value);
                break;
            case HORIZONTAL:
                scrollTo(value, 0);
                break;
        }
    }

    protected int getPullToRefreshScrollDuration() {
        return SMOOTH_SCROLL_DURATION_MS;
    }

    protected int getPullToRefreshScrollDurationLonger() {
        return SMOOTH_SCROLL_LONG_DURATION_MS;
    }

    /**
     * Smooth Scroll to position using the default duration of
     * {@value #SMOOTH_SCROLL_DURATION_MS} ms.
     *
     * @param scrollValue - Position to scroll to
     */
    protected final void smoothScrollTo(int scrollValue) {
        smoothScrollTo(scrollValue, getPullToRefreshScrollDuration());
    }

    /**
     * Smooth Scroll to position using the default duration of
     * {@value #SMOOTH_SCROLL_DURATION_MS} ms.
     *
     * @param scrollValue - Position to scroll to
     * @param listener    - Listener for scroll
     */
    protected final void smoothScrollTo(int scrollValue, OnSmoothScrollFinishedListener listener) {
        smoothScrollTo(scrollValue, getPullToRefreshScrollDuration(), 0, listener);
    }

    /**
     * Smooth Scroll to position using the longer default duration of
     * {@value #SMOOTH_SCROLL_LONG_DURATION_MS} ms.
     *
     * @param scrollValue - Position to scroll to
     */
    protected final void smoothScrollToLonger(int scrollValue) {
        smoothScrollTo(scrollValue, getPullToRefreshScrollDurationLonger());
    }

    private final void smoothScrollTo(int scrollValue, long duration) {
        smoothScrollTo(scrollValue, duration, 0, null);
    }

    private final void smoothScrollTo(int newScrollValue, long duration, long delayMillis,
                                      OnSmoothScrollFinishedListener listener) {
        if (null != mCurrentSmoothScrollRunnable) {
            mCurrentSmoothScrollRunnable.stop();
        }

        final int oldScrollValue;
        switch (getPullToRefreshScrollDirection()) {
            case HORIZONTAL:
                oldScrollValue = getScrollX();
                break;
            case VERTICAL:
            default:
                oldScrollValue = getScrollY();
                break;
        }

        if (oldScrollValue != newScrollValue) {
            if (null == mScrollAnimationInterpolator) {
                // Default interpolator is a Decelerate Interpolator
                mScrollAnimationInterpolator = new DecelerateInterpolator();
            }
            mCurrentSmoothScrollRunnable = new SmoothScrollRunnable(oldScrollValue, newScrollValue, duration, listener);

            if (delayMillis > 0) {
                postDelayed(mCurrentSmoothScrollRunnable, delayMillis);
            } else {
                post(mCurrentSmoothScrollRunnable);
            }
        }
    }

    private LayoutParams getLoadingLayoutLayoutParams() {
        switch (getPullToRefreshScrollDirection()) {
            case HORIZONTAL:
                return new LayoutParams(LayoutParams.WRAP_CONTENT,
                        LayoutParams.MATCH_PARENT);
            case VERTICAL:
            default:
                return new LayoutParams(LayoutParams.MATCH_PARENT,
                        LayoutParams.WRAP_CONTENT);
        }
    }

    protected void updateUIForMode() {
        // We need to use the correct LayoutParam values, based on scroll
        // direction
        final LayoutParams lp = getLoadingLayoutLayoutParams();

        // Remove Header, and then add Header Loading View again if needed
        if (this == mHeaderLayout.getParent()) {
            removeView(mHeaderLayout);
        }
        if (mMode.showHeaderLoadingLayout()) {
            addViewInternal(mHeaderLayout, 0, lp);
        }

        // Remove Footer, and then add Footer Loading View again if needed
        if (this == mFooterLayout.getParent()) {
            removeView(mFooterLayout);
        }
        if (mMode.showFooterLoadingLayout()) {
            addViewInternal(mFooterLayout, lp);
        }

        // Hide Loading Views
        refreshLoadingViewsSize();

        // If we're not using Mode.BOTH, set mCurrentMode to mMode, otherwise
        // set it to pull down
        mCurrentMode = (mMode != PullToRefreshBase.Mode.BOTH) ? mMode : PullToRefreshBase.Mode.PULL_FROM_START;
    }

    private int getMaximumPullScroll() {
        switch (getPullToRefreshScrollDirection()) {
            case HORIZONTAL:
                return Math.round(getWidth() / FRICTION);
            case VERTICAL:
            default:
                return Math.round(getHeight() / FRICTION);
        }
    }

    /***************************/
    final class SmoothScrollRunnable implements Runnable {
        private final Interpolator mInterpolator;
        private final int mScrollToY;
        private final int mScrollFromY;
        private final long mDuration;
        private OnSmoothScrollFinishedListener mListener;

        private boolean mContinueRunning = true;
        private long mStartTime = -1;
        private int mCurrentY = -1;

        public SmoothScrollRunnable(int fromY, int toY, long duration, OnSmoothScrollFinishedListener listener) {
            mScrollFromY = fromY;
            mScrollToY = toY;
            mInterpolator = mScrollAnimationInterpolator;
            mDuration = duration;
            mListener = listener;
        }

        @Override
        public void run() {

            /**
             * Only set mStartTime if this is the first time we're starting,
             * else actually calculate the Y delta
             */
            if (mStartTime == -1) {
                mStartTime = System.currentTimeMillis();
            } else {

                /**
                 * We do do all calculations in long to reduce software float
                 * calculations. We use 1000 as it gives us good accuracy and
                 * small rounding errors
                 */
                long normalizedTime = (1000 * (System.currentTimeMillis() - mStartTime)) / mDuration;
                normalizedTime = Math.max(Math.min(normalizedTime, 1000), 0);

                final int deltaY = Math.round((mScrollFromY - mScrollToY)
                        * mInterpolator.getInterpolation(normalizedTime / 1000f));
                mCurrentY = mScrollFromY - deltaY;
                setHeaderScroll(mCurrentY);
            }

            // If we're not at the target Y, keep going...
            if (mContinueRunning && mScrollToY != mCurrentY) {
                ViewCompat.postOnAnimation(StickyNavLayout2.this, this);
            } else {
                if (null != mListener) {
                    mListener.onSmoothScrollFinished();
                }
            }
        }

        public void stop() {
            mContinueRunning = false;
            removeCallbacks(this);
        }
    }

    static interface OnSmoothScrollFinishedListener {
        void onSmoothScrollFinished();
    }

    public static enum AnimationStyle {
        /**
         * This is the default for Android-PullToRefresh. Allows you to use any
         * drawable, which is automatically rotated and used as a Progress Bar.
         */
        ROTATE,

        /**
         * This is the old default, and what is commonly used on iOS. Uses an
         * arrow image which flips depending on where the user has scrolled.
         */
        FLIP;

        static AnimationStyle getDefault() {
            return ROTATE;
        }

        /**
         * Maps an int to a specific mode. This is needed when saving state, or
         * inflating the view from XML where the mode is given through a attr
         * int.
         *
         * @param modeInt - int to map a Mode to
         * @return Mode that modeInt maps to, or ROTATE by default.
         */
        static AnimationStyle mapIntToValue(int modeInt) {
            switch (modeInt) {
                case 0x0:
                default:
                    return ROTATE;
                case 0x1:
                    return FLIP;
            }
        }

        LoadingLayout createLoadingLayout(Context context, PullToRefreshBase.Mode mode, PullToRefreshBase.Orientation scrollDirection, TypedArray attrs) {
            switch (this) {
                case ROTATE:
                default:
                    return new RotateLoadingLayout(context, mode, scrollDirection, attrs);
                case FLIP:
                    return new FlipLoadingLayout(context, mode, scrollDirection, attrs);
            }
        }
    }


    // ===========================================================
    // Inner, Anonymous Classes, and Enumerations
    // ===========================================================

    /**
     * Simple Listener that allows you to be notified when the user has scrolled
     * to the end of the AdapterView. See (
     * {@link PullToRefreshAdapterViewBase#setOnLastItemVisibleListener}.
     *
     * @author Chris Banes
     */
    public static interface OnLastItemVisibleListener {

        /**
         * Called when the user has scrolled to the end of the list
         */
        public void onLastItemVisible();

    }

    /**
     * Listener that allows you to be notified when the user has started or
     * finished a touch event. Useful when you want to append extra UI events
     * (such as sounds). See (
     * {@link PullToRefreshAdapterViewBase # setOnPullEventListener}.
     *
     * @author Chris Banes
     */
    public static interface OnPullEventListener<V extends View> {

        /**
         * Called when the internal state has been changed, usually by the user
         * pulling.
         *
         * @param state - The new state of View.
         */
        public void onPullEvent(final StickyNavLayout2 layout2, PullToRefreshBase.State state, PullToRefreshBase.Mode direction);

    }

    /**
     * Simple Listener to listen for any callbacks to Refresh.
     *
     * @author Chris Banes
     */
    public static interface OnRefreshListener {

        /**
         * onRefresh will be called for both a Pull from start, and Pull from
         * end
         */
        public void onRefresh(final StickyNavLayout2 layout2);

    }

    /**
     * An advanced version of the Listener to listen for callbacks to Refresh.
     * This listener is different as it allows you to differentiate between Pull
     * Ups, and Pull Downs.
     *
     * @author Chris Banes
     */
    public static interface OnRefreshListener2 {
        // TODO These methods need renaming to START/END rather than DOWN/UP

        /**
         * onPullDownToRefresh will be called only when the user has Pulled from
         * the start, and released.
         */
        public void onPullDownToRefresh(final StickyNavLayout2 layout2);

        /**
         * onPullUpToRefresh will be called only when the user has Pulled from
         * the end, and released.
         */
        public void onPullUpToRefresh(final StickyNavLayout2 layout2);

    }
}
